/**
@file
Contains Split functions and their scanner function objects.

Version:
0.0.1

Last updated date:
2012-09-19
	
Created date:
2012-09-19
*/

#pragma once
#include <string>


namespace Zaf {
namespace String {



/**
Represents a function object used in Split function,
to determine a single character splitter.
*/
template<typename CharType>
struct CharSplitterScanner {

public:

	/**
	Initializes a new instance with specified character.

	@param ch Character as splitter.
	*/
	CharSplitterScanner(CharType ch) { 
		m_ch = ch;
	}

	/**
	Determines whether the specified character is splitter.

	@param ch Character to be determined.
	@return 1 if \a ch is splitter; 0 otherwise.
	*/
	unsigned int operator()(CharType ch) {
		return m_ch == ch ? 1 : 0;
	}

private:
	CharType m_ch;
};



/**
Represents a function object used in Split function,
to determine a several characters splitter.
*/
template<typename CharType>
struct CharArraySplitterScanner {

public:

	/**
	Initializes a new instance with specified character sequence,
	which represented by a string.

	@param arr Character sequence.
	*/
	CharArraySplitterScanner(const std::basic_string<CharType>& arr) : m_array(arr) {

	}

	/**
	Determines whether the specified character is the one in character sequence.

	@param ch Character to be determined.
	@return 1 if \a ch in character sequence; 0 otherwise.
	*/
	unsigned int operator()(CharType ch) {
		return m_array.find(ch) != -1 ? 1 : 0; 
	}

private:
	std::basic_string<CharType> m_array;
};



/**
Represents a function object used in Split function,
to determine a string splitter.
*/
template<typename CharType>
struct StringSplitterScanner {

public:
	
	/**
	Initializes a new instance with specified string.

	@param str %String as a splitter.
	*/
	StringSplitterScanner(const std::basic_string<CharType>& str) : m_str(str), m_matchLen(0) {	}

	/**
	Determines whether the specified character, along with previous characters,
	matches the string.

	@param ch Character to be determined.
	@return The length of string if they are matched; 0 otherwise.
	*/
	unsigned int operator()(CharType ch) {

		if (m_str.empty() == true) {
			return 0;
		}

		if (m_str[m_matchLen] == ch) {
			++m_matchLen;
		}
		else {
			m_matchLen = 0;
		}

		if (m_matchLen == m_str.length()) {
			m_matchLen = 0;
			return m_str.length();
		}

		return 0;
	}

private:
	unsigned int m_matchLen;
	std::basic_string<CharType> m_str;
};



/**
This is an overload version of %Split, for c-style string.

@see Split(const std::basic_string<CharType>&, OutputIteratorType, SplitterScannerType)
*/
template<typename CharType, typename OutputIteratorType, typename SplitterScannerType>
void Split(const CharType* str, OutputIteratorType outputIt, SplitterScannerType scanner) {

	const CharType* pre = str;
	const CharType* cur = str;

	while (*cur != 0) {

		unsigned int splitterLen = scanner(*cur);

		if (splitterLen != 0) {

			*outputIt = std::basic_string<CharType>(pre, cur - splitterLen + 1);

			pre = cur + 1;
		}

		++cur;
	}

	*outputIt = std::basic_string<CharType>(pre);
}



/**
Splits a string with specified rule.

@param str %String to be splitted.
@param outputIt Output iterator.
@param scanner A function pointer or function object, which determines the splitter.

This function uses output iterator, whose element must be 
std::string, to return results.

Split uses functions or function objects called scanner to
determinate whether there is a splitter. Scanners accept a
character parameter, and return splitter's length. Split
enumerates all characters in \a str to call \a scanner one by
one, to get the length of splitter.

Because Split calls scanner with only one character each time,
scanners should preserve the scanning state itself. Once the 
character sequence fits a whole splitter, scanners clear the 
state and return the length. Otherwise they change the state 
and return 0.
	
There are three predefined scanners: single char splitter,
several chars splitter and string splitter respectively. If 
user want a specifal scanner, he can implement a new one
himself.

@see CharSplitterScanner
@see CharArraySplitterScanner
@see StringSplitterScanner
*/
template<typename CharType, typename OutputIteratorType, typename SplitterScannerType>
inline void Split(const std::basic_string<CharType>& str, OutputIteratorType outputIt, SplitterScannerType scanner) {
	Split(str.c_str(), outputIt, scanner);
}


}
}